#include "stdlib.h"
#include "GoogleSREngine.h"
#include "curl.h"
#include "CommonUtil.h"

#define DEFAULT_GOOGLE_RESULT_CONF 0.5

static size_t http_callback(char *ptr, size_t count, size_t blocksize, void *userdata);

GoogleSREngine::GoogleSREngine() : SpeechRecognizer(SR_ENGINE_GOOGLE)
{
	m_useProxy = 0;
	m_proxyPort = 0;
	m_nBest = 0;
}

GoogleSREngine::~GoogleSREngine(void)
{
}

bool GoogleSREngine::initialize(void)
{
	if (!m_pConfigurator->getStringValue(MODULE_SpeechManager, "google-sr", "lang", m_lang)) {
		m_pLogger->err(MODULE_SpeechManager, "GoogleSREngine", "lang is not defined.");
		return false;
	}

	m_nBest = 5;
	m_pConfigurator->getIntValue(MODULE_SpeechManager, "google-sr", "nbest", m_nBest);

	m_useProxy = false;
	m_pConfigurator->getBoolValue(MODULE_OTHER, "proxy", "https.proxySet", m_useProxy);
	if (m_useProxy) 
	{
		if (!m_pConfigurator->getStringValue(MODULE_OTHER, "proxy", "https.proxyHost", m_proxyAddress)) {
			m_pLogger->err(MODULE_SpeechManager, "GoogleSREngine", "https.proxyHost is not defined.");
			return false;
		}
		if (!m_pConfigurator->getIntValue(MODULE_OTHER, "proxy", "https.proxyPort", m_proxyPort)) {
			m_pLogger->err(MODULE_SpeechManager, "GoogleSREngine", "https.proxyPort is not defined.");
			return false;
		}
		if (!m_pConfigurator->getStringValue(MODULE_OTHER, "proxy", "https.proxyDomain", m_proxyDomain)) {
			m_pLogger->err(MODULE_SpeechManager, "GoogleSREngine", "https.proxyDomain is not defined.");
			return false;
		}
		if (!m_pConfigurator->getStringValue(MODULE_OTHER, "proxy", "https.proxyUser", m_proxyUser)) {
			m_pLogger->err(MODULE_SpeechManager, "GoogleSREngine", "https.proxyUser is not defined.");
			return false;
		}
		if (!m_pConfigurator->getStringValue(MODULE_OTHER, "proxy", "https.proxyPassword", m_proxyPassword)) {
			m_pLogger->err(MODULE_SpeechManager, "GoogleSREngine", "https.proxyPassword is not defined.");
			return false;
		}
	}
	m_googleURL = "https://www.google.com/speech-api/v1/recognize?xjerr=1&client=chrome&lang=" + m_lang + "&maxresults=" + toStr(m_nBest);

	m_isInitialized = true;
	return true;
}

SRError GoogleSREngine::recFromFile(const char* filename, SRNLUResult* result)
{
	char convertedFile[4096];
	strcpy(convertedFile, filename);
	strcat(convertedFile, ".tmp.google");

	convertFormat(filename, convertedFile);

	FILE *file;
	file = fopen(convertedFile, "rb");
	if (!file) {
		UILogger::getInstance()->err(MODULE_SpeechManager, "GoogleSREngine", "Failed to open audio file: " + std::string(convertedFile));
		return SR_ERROR_AUDIOSTREAM_FAIL_TO_START;
	}
	
	fseek(file, 0, SEEK_END);
	int length = ftell(file);
	fseek(file, 0, SEEK_SET);

	unsigned char* data = new unsigned char[length];
	if (!data) {
		UILogger::getInstance()->err(MODULE_SpeechManager, "GoogleSREngine", "Memory allocation error");
		fclose(file);
		return SR_ERROR_AUDIOSTREAM_FAIL_TO_START;
	}

	int ret;
	ret = fread(data, length, sizeof(unsigned char), file);
	if (ret) return SR_ERROR_UNKNOWN;
	fclose(file);

	CURL *conn_hndl;
	struct curl_httppost *form, *lastptr;
	struct curl_slist *headers;
	struct sprec_server_response *resp;
	char errstr[CURL_ERROR_SIZE];

	resp = new sprec_server_response;
	resp->data = new char[4096];
	resp->length = 0;
	conn_hndl = curl_easy_init();
	form = NULL;
	lastptr = NULL;
	headers = NULL;
	headers = curl_slist_append(headers, "Content-Type: audio/x-flac; rate=16000");
	curl_formadd(&form, &lastptr, CURLFORM_COPYNAME, "myfile", CURLFORM_CONTENTSLENGTH, length, CURLFORM_PTRCONTENTS, data, CURLFORM_END);

	curl_easy_setopt(conn_hndl, CURLOPT_URL, m_googleURL.c_str());
	curl_easy_setopt(conn_hndl, CURLOPT_HTTPHEADER, headers);
	curl_easy_setopt(conn_hndl, CURLOPT_HTTPPOST, form);
	curl_easy_setopt(conn_hndl, CURLOPT_WRITEFUNCTION, http_callback);
	curl_easy_setopt(conn_hndl, CURLOPT_WRITEDATA, resp);
	curl_easy_setopt(conn_hndl, CURLOPT_SSL_VERIFYPEER, 0);
	if (m_useProxy) {
		std::string proxy		 = m_proxyAddress + ":" + toStr(m_proxyPort);
		std::string proxyUserPwd = m_proxyDomain + "\\" + m_proxyUser + ":" + m_proxyPassword;
		curl_easy_setopt(conn_hndl, CURLOPT_PROXY, proxy.c_str());
		curl_easy_setopt(conn_hndl, CURLOPT_PROXYUSERPWD, proxyUserPwd.c_str());
	}
	curl_easy_setopt(conn_hndl, CURLOPT_ERRORBUFFER, errstr); 

	resp->data[resp->length] = '\0';

	curl_formfree(form);
	curl_slist_free_all(headers);
	curl_easy_cleanup(conn_hndl);

	//TODO:: parse GOOGlE result
	std::string resultString = std::string(resp->data);
	if (resultString.find("{\"status\":0,") == std::string::npos) {
		UILogger::getInstance()->debug(MODULE_SpeechManager, "GoogleSREngine", "Google Error: " + resultString);
		return SR_ERROR_NOT_RECOGNIZED;
	}

	int idx = 1;
	unsigned start = resultString.find("\"utterance\":\"");
	while (start != std::string::npos)
	{
		unsigned end = resultString.find("\"", start+13);
		if (end != std::string::npos) {
			SRHypothesis hyp;

			hyp.srSource = SR_ENGINE_GOOGLE;
			hyp.srHypID = idx++;

			std::string sentence = resultString.substr(start+13, end-start-13);
			strcpy(hyp.sentence, sentence.c_str());

			std::vector<std::string> words;
			splitString(&sentence, " ", &words);
			hyp.numOfWords = words.size();
			for (int iWord=0; iWord<(int)words.size(); iWord++) {
				SRHypWord word;
				word.wordUserID = -1;
				strcpy(word.word, words[iWord].c_str());
				word.wordConf = 1.0;
				word.wordProb = 1.0;
				word.wordLMScore = 1.0;
				word.wordStartTime = -1.0;
				word.wordEndTime = -1.0;
				word.isSRSlot = false;
				strcpy(word.srSlotName, "");
				hyp.sentWords[iWord] = word;
			}

			hyp.sentConf = -1;
			start = resultString.find("\"confidence\":", end);
			if (start != std::string::npos)
			{
				end = resultString.find("}", start+13);
				if (end != std::string::npos)
					hyp.sentConf = atof(resultString.substr(start+13, end-start-13).c_str());
				else {
					m_pLogger->err(MODULE_SpeechManager, "GoogleSREngine", "Invalid Google SR JSON result: " + resultString);
					return SR_ERROR_NOT_RECOGNIZED;
				}
			}

			if (idx > 2)
				hyp.sentConf = DEFAULT_GOOGLE_RESULT_CONF;

			hyp.sentLMScore		= 1.0;
			hyp.sentProb		= 1.0;

			hyp.sentStartTime	= -1.0;
			hyp.sentEndTime		= -1.0;
			start = resultString.find("\"utterance\":\"", end);
			result->addSRHyp(hyp);
		}
		else {
			m_pLogger->err(MODULE_SpeechManager, "GoogleSREngine", "Invalid Google SR JSON result: " + resultString);
			return SR_ERROR_NOT_RECOGNIZED;
		}
	}
	
	delete resp->data;
	delete resp;
	delete data;

	return SR_SUCCESS;
}

SRError GoogleSREngine::recFromMic(int sessionID, const char* logWavFileName, SRNLUResult *result)
{
	return SR_ERROR_RECORD_DEVICE_FAIL_TO_CREATE;
}

bool GoogleSREngine::stopRecording(int sessionID)
{
	return false;
}

static size_t http_callback(char *ptr, size_t count, size_t blocksize, void *userdata)
{
	struct sprec_server_response *response = (sprec_server_response*) userdata;
	size_t size = count * blocksize;
	memcpy(response->data + response->length, ptr, size);
	response->length += size;
	return size;
}
